#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/prctl.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>

#include <imp/imp_audio.h>
#include <imp/imp_log.h>

#include "testopt.h"

#define TAG "test_ao"

#define AO_TEST_SAMPLE_RATE 16000
#define AO_TEST_SAMPLE_TIME 50
#define AO_TEST_BUF_SIZE (AO_TEST_SAMPLE_RATE * sizeof(short) * AO_TEST_SAMPLE_TIME / 1000)
#define AO_BASIC_TEST_PLAY_FILE  "./ao_paly.pcm"

extern TEST_ARGS testArgs;   /*Save input param*/
extern char fileNameBuf[NAME_BUF_SIZE];

static char inFileName[256] = "test_ao.pcm";

static int sampleRate = 16000;
static int frmNum = 20;
static int numPerFrm = 640;

static int aoVol = 60;
static int aoGain = 28;
static int hpfFlag = 0;  /*enable hpf flag;*/
static int agcFlag = 0;  /*enable agc flag;*/
static int bufSize = 1280;

IMPAudioAgcConfig agcConfig = {
	.TargetLevelDbfs = 0,
	.CompressionGaindB = 9
};

static void *ao_test_play_thread(void *argv)
{
	unsigned char *buf = NULL;
	int size = 0;
	int ret = -1;

	buf = (unsigned char *)malloc(bufSize);
	if (buf == NULL) {
		IMP_LOG_ERR(TAG, "[ERROR] %s: malloc audio buf error\n", __func__);
		return NULL;
	}

	FILE *play_file = fopen(inFileName, "rb");
	if (play_file == NULL) {
		IMP_LOG_ERR(TAG, "[ERROR] %s: fopen %s failed\n", __func__, inFileName);
		return NULL;
	}

	/* Step 1: set public attribute of AO device. */
	int devID = 0;
	IMPAudioIOAttr attr;
	attr.samplerate = sampleRate;
	attr.bitwidth = AUDIO_BIT_WIDTH_16;
	attr.soundmode = AUDIO_SOUND_MODE_MONO;
	attr.frmNum = 20;
	attr.numPerFrm = numPerFrm;
	attr.chnCnt = 1;
	ret = IMP_AO_SetPubAttr(devID, &attr);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "set ao %d attr err: %d\n", devID, ret);
		return NULL;
	}

	memset(&attr, 0x0, sizeof(attr));
	ret = IMP_AO_GetPubAttr(devID, &attr);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "get ao %d attr err: %d\n", devID, ret);
		return NULL;
	}

	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr samplerate:%d\n", attr.samplerate);
	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr   bitwidth:%d\n", attr.bitwidth);
	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr  soundmode:%d\n", attr.soundmode);
	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr     frmNum:%d\n", attr.frmNum);
	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr  numPerFrm:%d\n", attr.numPerFrm);
	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr     chnCnt:%d\n", attr.chnCnt);

	/* Step 2: enable AO device. */
	ret = IMP_AO_Enable(devID);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "enable ao %d err\n", devID);
		return NULL;
	}

	/* Step 3: enable AI channel. */
	int chnID = 0;
	ret = IMP_AO_EnableChn(devID, chnID);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "Audio play enable channel failed\n");
		return NULL;
	}

	/* Step 4: Set audio channel volume. */
	int chnVol = aoVol;
	ret = IMP_AO_SetVol(devID, chnID, chnVol);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Play set volume failed\n");
		return NULL;
	}

	ret = IMP_AO_GetVol(devID, chnID, &chnVol);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Play get volume failed\n");
		return NULL;
	}
	IMP_LOG_INFO(TAG, "Audio Out GetVol    vol:%d\n", chnVol);

	int aogain = aoGain;
	ret = IMP_AO_SetGain(devID, chnID, aogain);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Record Set Gain failed\n");
		return NULL;
	}

	ret = IMP_AO_GetGain(devID, chnID, &aogain);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Record Get Gain failed\n");
		return NULL;
	}
	IMP_LOG_INFO(TAG, "Audio Out GetGain    gain : %d\n", aogain);
	if(1 == hpfFlag) {
		ret = IMP_AO_EnableHpf(&attr);
		if(ret != 0) {
			printf("enable audio hpf error.\n");
			IMP_LOG_INFO(TAG, "enable audio hpf error.\n");
			return NULL;
		}
	}

	if(1 == agcFlag) {
		ret = IMP_AO_EnableAgc(&attr, agcConfig);
		if(ret != 0) {
			printf("enable audio agc error.\n");
			IMP_LOG_INFO(TAG, "enable audio agc error.\n");
			return NULL;
		}
	}

	int i = 0;
	while (1) {
		size = fread(buf,1,bufSize,play_file);
		if (size < bufSize)
			break;

		/* Step 5: send frame data. */
		IMPAudioFrame frm;
		frm.virAddr = (uint32_t *)buf;
		frm.len = size;
		ret = IMP_AO_SendFrame(devID, chnID, &frm, BLOCK);
		if (ret != 0) {
			IMP_LOG_ERR(TAG, "send Frame Data error\n");
			return NULL;
		}

		IMPAudioOChnState play_status;
		ret = IMP_AO_QueryChnStat(devID, chnID, &play_status);
		if (ret != 0) {
			IMP_LOG_ERR(TAG, "IMP_AO_QueryChnStat error\n");
			return NULL;
		}

		IMP_LOG_INFO(TAG, "Play: TotalNum %d, FreeNum %d, BusyNum %d\n",
				play_status.chnTotalNum, play_status.chnFreeNum, play_status.chnBusyNum);

		if (++i == 40) {
			ret = IMP_AO_PauseChn(devID, chnID);
			if (ret != 0) {
				IMP_LOG_ERR(TAG, "IMP_AO_PauseChn error\n");
				return NULL;
			}

			printf("[INFO] Test : Audio Play Pause test.\n");
			printf("[INFO]      : Please input any key to continue.\n");
			getchar();

			ret = IMP_AO_ClearChnBuf(devID, chnID);
			if (ret != 0) {
				IMP_LOG_ERR(TAG, "IMP_AO_ClearChnBuf error\n");
				return NULL;
			}

			ret = IMP_AO_ResumeChn(devID, chnID);
			if (ret != 0) {
				IMP_LOG_ERR(TAG, "IMP_AO_ResumeChn error\n");
				return NULL;
			}
		}
	}
	ret = IMP_AO_FlushChnBuf(devID, chnID);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "IMP_AO_FlushChnBuf error\n");
		return NULL;
	}
	 if(1 == hpfFlag) {
		ret = IMP_AO_DisableHpf();
		if(ret != 0) {
			printf("disable audio hpf error.\n");
			IMP_LOG_INFO(TAG, "disable audio hpf error.\n");
			return NULL;
		}
	}
	if(1 == agcFlag) {
		ret = IMP_AO_DisableAgc();
		if(ret != 0) {
			printf("disable audio agc error.\n");
			IMP_LOG_INFO(TAG, "disable audio agc error.\n");
			return NULL;
		}
	}

	/* Step 6: disable the audio channel. */
	ret = IMP_AO_DisableChn(devID, chnID);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "Audio channel disable error\n");
		return NULL;
	}

	/* Step 7: disable the audio devices. */
	ret = IMP_AO_Disable(devID);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "Audio device disable error\n");
		return NULL;
	}

	fclose(play_file);
	free(buf);
	pthread_exit(0);
}

static void set_aoTest_param(TEST_ARGS *pTestArgs)
{
	if(NULL == pTestArgs){
		printf("fun:%s,pTestArgs is empty.\n",__func__);
		return;
	}

	if(strcmp(pTestArgs->inFileName,"")!= 0)
		strncpy(inFileName,pTestArgs->inFileName,MAX_FILE_NAME_LEN);

	if(pTestArgs->sampleRate > 0)
		sampleRate = pTestArgs->sampleRate;

	if(pTestArgs->frmNum > 0)
		frmNum = pTestArgs->frmNum;

	if(pTestArgs->sampleRate > 0)
		sampleRate = pTestArgs->sampleRate;

	if(pTestArgs->numPerFrm > 0)
		numPerFrm = pTestArgs->numPerFrm;
	else
		numPerFrm = (sampleRate/100)*4;

	bufSize = numPerFrm*sizeof(short);

	if(pTestArgs->vol != 60)
		aoVol = pTestArgs->vol;

	if(pTestArgs->gain != 28)
		aoGain = pTestArgs->gain;

	if(((pTestArgs->agcConfig).TargetLevelDbfs != -1)&&((pTestArgs->agcConfig).CompressionGaindB != -1)){
		agcConfig.TargetLevelDbfs = (pTestArgs->agcConfig).TargetLevelDbfs;
		agcConfig.CompressionGaindB = (pTestArgs->agcConfig).CompressionGaindB;
		agcFlag = 1;
	}
	 if(pTestArgs->hpfFlag != 0)
		 hpfFlag = pTestArgs->hpfFlag;
}

int main(int argc,char *argv[])
{
	int ret = -1;

	ret = parse_param(argc,argv);
	if(1 == ret)
		return 0;

	if(ret < 0){
		printf("[ERROR]:parse param failed\n");
		return -1;
	} else {
		printf("[INFO]:parse param Ok\n");
	}
	set_aoTest_param(&testArgs);

	printf("[INFO]:inFileName = %s\n",inFileName);
	printf("[INFO]:sampleRate = %d\n",sampleRate);
	printf("[INFO]:frmNum = %d\n",frmNum);
	printf("[INFO]:numPerFrm = %d\n",numPerFrm);
	printf("[INFO]:aoVol = %d\n",aoVol);
	printf("[INFO]:aoGain = %d\n",aoGain);
	printf("[INFO]:bufSize = %d\n",bufSize);

	if(1 == hpfFlag)
		printf("[INFO]:HPF is enabled\n");

	if(1 == agcFlag) {
		printf("[INFO]:agcConfig.TargetLevelDbfs = %d\n",agcConfig.TargetLevelDbfs);
		printf("[INFO]:agcConfig.CompressionGaindB = %d\n",agcConfig.CompressionGaindB);
	}

	printf("[INFO] Test 1: Start audio play test.\n");
	printf("[INFO]        : Please input any key to continue.\n");
	getchar();

	pthread_t tid_play;
	ret = pthread_create(&tid_play,NULL,ao_test_play_thread,NULL);
	if(ret != 0) {
		printf("Create _ao_test_play_thread failed\n");
		return -1;
	}
	pthread_join(tid_play,NULL);

	return 0;
}
